#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
微信公众号素材管理工具
支持上传永久素材：图片、语音、视频、缩略图
"""

import requests
import json
import os
import mimetypes
from typing import Optional, Dict, Any
from wechat_token import WeChatTokenManager

class WeChatMaterialManager:
    """
    微信公众号素材管理类
    """
    
    def __init__(self, token_manager: WeChatTokenManager):
        """
        初始化
        :param token_manager: Token管理器实例
        """
        self.token_manager = token_manager
        self.base_url = "https://api.weixin.qq.com/cgi-bin/material"
    
    def upload_permanent_material(self, 
                                file_path: str, 
                                material_type: str,
                                title: str = None,
                                introduction: str = None) -> Dict[str, Any]:
        """
        上传永久素材
        :param file_path: 文件路径
        :param material_type: 素材类型 (image, voice, video, thumb)
        :param title: 视频标题（仅视频素材需要）
        :param introduction: 视频简介（仅视频素材需要）
        :return: 上传结果
        """
        # 验证文件是否存在
        if not os.path.exists(file_path):
            raise FileNotFoundError(f"文件不存在: {file_path}")
        
        # 验证素材类型
        valid_types = ['image', 'voice', 'video', 'thumb']
        if material_type not in valid_types:
            raise ValueError(f"无效的素材类型: {material_type}，支持的类型: {valid_types}")
        
        # 验证文件格式和大小
        self._validate_file(file_path, material_type)
        
        # 获取访问令牌
        access_token = self.token_manager.get_access_token()
        
        # 构建请求URL
        url = f"{self.base_url}/add_material"
        params = {
            'access_token': access_token,
            'type': material_type
        }
        
        # 准备文件数据
        files = {}
        data = {}
        
        # 读取文件
        with open(file_path, 'rb') as f:
            file_content = f.read()
            file_name = os.path.basename(file_path)
            mime_type = mimetypes.guess_type(file_path)[0] or 'application/octet-stream'
            
            files['media'] = (file_name, file_content, mime_type)
        
        # 如果是视频素材，添加描述信息
        if material_type == 'video':
            if not title or not introduction:
                raise ValueError("视频素材必须提供标题和简介")
            
            description = {
                'title': title,
                'introduction': introduction
            }
            data['description'] = json.dumps(description, ensure_ascii=False)
        
        try:
            print(f"正在上传{material_type}素材: {file_name}")
            
            # 发送请求
            response = requests.post(url, params=params, files=files, data=data, timeout=60)
            response.raise_for_status()
            
            result = response.json()
            
            # 检查是否有错误
            if 'errcode' in result and result['errcode'] != 0:
                raise Exception(f"上传失败: {result['errcode']} - {result.get('errmsg', '未知错误')}")
            
            print(f"✅ 素材上传成功!")
            print(f"Media ID: {result.get('media_id')}")
            if result.get('url'):
                print(f"素材URL: {result.get('url')}")
            
            return result
            
        except requests.RequestException as e:
            raise Exception(f"网络请求失败: {e}")
        except json.JSONDecodeError as e:
            raise Exception(f"解析响应失败: {e}")
    
    def _validate_file(self, file_path: str, material_type: str):
        """
        验证文件格式和大小
        :param file_path: 文件路径
        :param material_type: 素材类型
        """
        file_size = os.path.getsize(file_path)
        file_ext = os.path.splitext(file_path)[1].lower()
        
        # 文件格式和大小限制
        limits = {
            'image': {
                'max_size': 10 * 1024 * 1024,  # 10MB
                'extensions': ['.bmp', '.png', '.jpeg', '.jpg', '.gif'],
                'description': '图片素材'
            },
            'voice': {
                'max_size': 2 * 1024 * 1024,   # 2MB
                'extensions': ['.mp3', '.wma', '.wav', '.amr'],
                'description': '语音素材'
            },
            'video': {
                'max_size': 10 * 1024 * 1024,  # 10MB
                'extensions': ['.mp4'],
                'description': '视频素材'
            },
            'thumb': {
                'max_size': 64 * 1024,         # 64KB
                'extensions': ['.jpg'],
                'description': '缩略图素材'
            }
        }
        
        limit = limits.get(material_type)
        if not limit:
            raise ValueError(f"不支持的素材类型: {material_type}")
        
        # 检查文件大小
        if file_size > limit['max_size']:
            size_mb = limit['max_size'] / (1024 * 1024)
            raise ValueError(f"{limit['description']}文件大小不能超过 {size_mb}MB，当前文件大小: {file_size / (1024 * 1024):.2f}MB")
        
        # 检查文件格式
        if file_ext not in limit['extensions']:
            raise ValueError(f"{limit['description']}支持的格式: {limit['extensions']}，当前文件格式: {file_ext}")
        
        print(f"✅ 文件验证通过: {os.path.basename(file_path)} ({file_size / 1024:.1f}KB)")
    
    def get_material_list(self, material_type: str, offset: int = 0, count: int = 20) -> Dict[str, Any]:
        """
        获取素材列表
        :param material_type: 素材类型 (image, voice, video, thumb, news)
        :param offset: 从全部素材中的该偏移位置开始返回，0表示从第一个开始
        :param count: 返回素材的数量，取值在1到20之间
        :return: 素材列表
        """
        access_token = self.token_manager.get_access_token()
        
        url = f"{self.base_url}/batchget_material"
        params = {'access_token': access_token}
        
        data = {
            'type': material_type,
            'offset': offset,
            'count': count
        }
        
        try:
            response = requests.post(url, params=params, json=data, timeout=30)
            response.raise_for_status()
            
            result = response.json()
            
            if 'errcode' in result and result['errcode'] != 0:
                raise Exception(f"获取素材列表失败: {result['errcode']} - {result.get('errmsg', '未知错误')}")
            
            return result
            
        except requests.RequestException as e:
            raise Exception(f"网络请求失败: {e}")
        except json.JSONDecodeError as e:
            raise Exception(f"解析响应失败: {e}")
    
    def get_material_count(self) -> Dict[str, Any]:
        """
        获取素材总数
        :return: 各类型素材的总数
        """
        access_token = self.token_manager.get_access_token()
        
        url = f"{self.base_url}/get_materialcount"
        params = {'access_token': access_token}
        
        try:
            response = requests.get(url, params=params, timeout=30)
            response.raise_for_status()
            
            result = response.json()
            
            if 'errcode' in result and result['errcode'] != 0:
                raise Exception(f"获取素材总数失败: {result['errcode']} - {result.get('errmsg', '未知错误')}")
            
            return result
            
        except requests.RequestException as e:
            raise Exception(f"网络请求失败: {e}")
        except json.JSONDecodeError as e:
            raise Exception(f"解析响应失败: {e}")
    
    def delete_material(self, media_id: str) -> bool:
        """
        删除永久素材
        :param media_id: 媒体文件ID
        :return: 删除是否成功
        """
        access_token = self.token_manager.get_access_token()
        
        url = f"{self.base_url}/del_material"
        params = {'access_token': access_token}
        
        data = {'media_id': media_id}
        
        try:
            response = requests.post(url, params=params, json=data, timeout=30)
            response.raise_for_status()
            
            result = response.json()
            
            if 'errcode' in result and result['errcode'] != 0:
                raise Exception(f"删除素材失败: {result['errcode']} - {result.get('errmsg', '未知错误')}")
            
            print(f"✅ 素材删除成功: {media_id}")
            return True
            
        except requests.RequestException as e:
            raise Exception(f"网络请求失败: {e}")
        except json.JSONDecodeError as e:
            raise Exception(f"解析响应失败: {e}")


def example():
    """
    使用示例
    """
    from config import WECHAT_CONFIG
    
    print("=== 微信公众号素材管理示例 ===")
    
    # 检查配置
    if WECHAT_CONFIG['APP_ID'] == 'your_app_id_here':
        print("⚠️  请先在config.py中配置您的APP_ID和APP_SECRET")
        return
    
    # 创建token管理器和素材管理器
    token_manager = WeChatTokenManager(
        WECHAT_CONFIG['APP_ID'], 
        WECHAT_CONFIG['APP_SECRET']
    )
    
    material_manager = WeChatMaterialManager(token_manager)
    
    try:
        print("\n1. 获取素材总数...")
        count_info = material_manager.get_material_count()
        print(f"素材总数: {json.dumps(count_info, ensure_ascii=False, indent=2)}")
        
        print("\n2. 获取图片素材列表...")
        image_list = material_manager.get_material_list('image', 0, 5)
        print(f"图片素材数量: {image_list.get('total_count', 0)}")
        
        # 上传素材示例（需要实际文件）
        # print("\n3. 上传图片素材...")
        # result = material_manager.upload_permanent_material(
        #     file_path='path/to/your/image.jpg',
        #     material_type='image'
        # )
        # print(f"上传结果: {result}")
        
        # 上传视频素材示例（需要实际文件）
        # print("\n4. 上传视频素材...")
        # result = material_manager.upload_permanent_material(
        #     file_path='path/to/your/video.mp4',
        #     material_type='video',
        #     title='测试视频',
        #     introduction='这是一个测试视频'
        # )
        # print(f"上传结果: {result}")
        
        print("\n✅ 示例运行完成！")
        
    except Exception as e:
        print(f"❌ 运行失败: {e}")


if __name__ == '__main__':
    example()
